"""
Cron表达式解析器和工具
提供Cron表达式验证、解析和下次执行时间计算功能
"""
import re
import logging
import json
import traceback
from datetime import datetime, timedelta
from croniter import croniter
from typing import Optional, List, Dict, Any
from services.shared.timezone_utils import now as timezone_now
from services.shared.log_utils import log_error, log_info, log_system_event


class CronParser:
    """Cron表达式解析器"""
    
    # 预定义的常用Cron表达式
    PREDEFINED_EXPRESSIONS = {
        '@yearly': '0 0 1 1 *',
        '@annually': '0 0 1 1 *',
        '@monthly': '0 0 1 * *',
        '@weekly': '0 0 * * 0',
        '@daily': '0 0 * * *',
        '@midnight': '0 0 * * *',
        '@hourly': '0 * * * *'
    }
    
    # Cron字段描述
    FIELD_NAMES = ['分钟', '小时', '日', '月', '星期']
    
    # 特殊值说明
    SPECIAL_VALUES = {
        '*': '任意值',
        '?': '不指定值',
        '-': '范围',
        ',': '列表',
        '/': '步长',
        'L': '最后',
        'W': '工作日',
        '#': '第几个'
    }
    
    def __init__(self):
        self.reset()
    
    def reset(self):
        """重置解析器状态"""
        self.expression = None
        self.fields = []
        self.is_valid = False
        self.error_message = None
        self.description = None
    
    def parse(self, expression: str) -> Dict[str, Any]:
        """
        解析Cron表达式
        
        Args:
            expression: Cron表达式字符串
            
        Returns:
            解析结果字典
        """
        self.reset()
        self.expression = expression.strip()
        
        try:
            # 处理预定义表达式
            if self.expression in self.PREDEFINED_EXPRESSIONS:
                self.expression = self.PREDEFINED_EXPRESSIONS[self.expression]
            
            # 验证表达式格式
            if not self._validate_format():
                return self._get_result()
            
            # 分割字段
            self.fields = self.expression.split()
            
            # 验证字段数量
            if len(self.fields) not in [5, 6]:
                self.error_message = f"Cron表达式字段数量错误，应为5个或6个字段，实际为{len(self.fields)}个"
                return self._get_result()
            
            # 如果是6个字段，忽略秒字段（第一个字段）
            if len(self.fields) == 6:
                self.fields = self.fields[1:]
            
            # 验证每个字段
            if not self._validate_fields():
                return self._get_result()
            
            # 使用croniter验证表达式
            if not self._validate_with_croniter():
                return self._get_result()
            
            # 生成描述
            self.description = self._generate_description()
            self.is_valid = True
            
        except Exception as e:
            self.error_message = f"解析表达式时发生错误: {str(e)}"
            
            # 记录详细的系统错误日志
            log_error(
                "cron_parser_parse_failed",
                {
                    "expression": expression,
                    "error_type": type(e).__name__,
                    "error_message": str(e),
                    "stack_trace": traceback.format_exc(),
                    "execution_context": {
                        "function": "parse",
                        "module": "scheduler.cron_parser",
                        "class": "CronParser"
                    }
                }
            )
        
        return self._get_result()
    
    def _validate_format(self) -> bool:
        """验证表达式基本格式"""
        if not self.expression:
            self.error_message = "Cron表达式不能为空"
            return False
        
        # 检查是否包含非法字符
        allowed_chars = set('0123456789*?-,/LW# ')
        if not set(self.expression).issubset(allowed_chars):
            invalid_chars = set(self.expression) - allowed_chars
            self.error_message = f"包含非法字符: {', '.join(invalid_chars)}"
            return False
        
        return True
    
    def _validate_fields(self) -> bool:
        """验证各个字段"""
        field_ranges = [
            (0, 59),   # 分钟
            (0, 23),   # 小时
            (1, 31),   # 日
            (1, 12),   # 月
            (0, 7)     # 星期 (0和7都表示周日)
        ]
        
        for i, (field, (min_val, max_val)) in enumerate(zip(self.fields, field_ranges)):
            if not self._validate_field(field, min_val, max_val, i):
                return False
        
        return True
    
    def _validate_field(self, field: str, min_val: int, max_val: int, field_index: int) -> bool:
        """验证单个字段"""
        if field == '*' or field == '?':
            return True
        
        # 处理逗号分隔的列表
        if ',' in field:
            parts = field.split(',')
            for part in parts:
                if not self._validate_field_part(part.strip(), min_val, max_val, field_index):
                    return False
            return True
        
        return self._validate_field_part(field, min_val, max_val, field_index)
    
    def _validate_field_part(self, part: str, min_val: int, max_val: int, field_index: int) -> bool:
        """验证字段的单个部分"""
        # 处理步长 (/)
        if '/' in part:
            base, step = part.split('/', 1)
            if not step.isdigit() or int(step) <= 0:
                self.error_message = f"第{field_index + 1}个字段步长值无效: {step}"
                return False
            part = base
        
        # 处理范围 (-)
        if '-' in part:
            start, end = part.split('-', 1)
            if not (start.isdigit() and end.isdigit()):
                self.error_message = f"第{field_index + 1}个字段范围值无效: {part}"
                return False
            
            start_val, end_val = int(start), int(end)
            if start_val > end_val:
                self.error_message = f"第{field_index + 1}个字段范围起始值大于结束值: {part}"
                return False
            
            if not (min_val <= start_val <= max_val and min_val <= end_val <= max_val):
                self.error_message = f"第{field_index + 1}个字段范围值超出有效范围[{min_val}-{max_val}]: {part}"
                return False
            
            return True
        
        # 处理数字值
        if part.isdigit():
            val = int(part)
            if not (min_val <= val <= max_val):
                # 特殊处理星期字段的7（表示周日）
                if field_index == 4 and val == 7:
                    return True
                self.error_message = f"第{field_index + 1}个字段值超出有效范围[{min_val}-{max_val}]: {val}"
                return False
            return True
        
        # 处理特殊字符（L, W, #等）
        if field_index == 2:  # 日字段
            if part.endswith('W') or part == 'L' or 'L' in part:
                return True
        
        if field_index == 4:  # 星期字段
            if '#' in part:
                return True
        
        self.error_message = f"第{field_index + 1}个字段格式无效: {part}"
        return False
    
    def _validate_with_croniter(self) -> bool:
        """使用croniter验证表达式"""
        try:
            # 将aware datetime转换为naive datetime用于croniter
            current_time = timezone_now()
            current_time_naive = current_time.replace(tzinfo=None)
            cron = croniter(self.expression, current_time_naive)
            # 尝试获取下一个执行时间
            cron.get_next(datetime)
            return True
        except (ValueError, TypeError) as e:
            self.error_message = f"Cron表达式格式错误: {str(e)}"
            return False
    
    def _generate_description(self) -> str:
        """生成Cron表达式的中文描述"""
        try:
            minute, hour, day, month, weekday = self.fields
            
            desc_parts = []
            
            # 处理分钟
            if minute == '*':
                minute_desc = "每分钟"
            elif '/' in minute:
                if minute.startswith('*/'):
                    step = minute.split('/')[1]
                    minute_desc = f"每{step}分钟"
                else:
                    minute_desc = f"在分钟{minute}"
            else:
                minute_desc = f"在第{minute}分钟"
            
            # 处理小时
            if hour == '*':
                hour_desc = "每小时"
            elif '/' in hour:
                if hour.startswith('*/'):
                    step = hour.split('/')[1]
                    hour_desc = f"每{step}小时"
                else:
                    hour_desc = f"在小时{hour}"
            else:
                hour_desc = f"在{hour}点"
            
            # 处理日
            if day == '*':
                day_desc = "每天"
            elif '/' in day:
                if day.startswith('*/'):
                    step = day.split('/')[1]
                    day_desc = f"每{step}天"
                else:
                    day_desc = f"在日期{day}"
            else:
                day_desc = f"在每月第{day}天"
            
            # 处理月
            if month == '*':
                month_desc = "每月"
            elif '/' in month:
                if month.startswith('*/'):
                    step = month.split('/')[1]
                    month_desc = f"每{step}个月"
                else:
                    month_desc = f"在月份{month}"
            else:
                month_names = ['', '1月', '2月', '3月', '4月', '5月', '6月',
                              '7月', '8月', '9月', '10月', '11月', '12月']
                if month.isdigit() and 1 <= int(month) <= 12:
                    month_desc = f"在{month_names[int(month)]}"
                else:
                    month_desc = f"在月份{month}"
            
            # 处理星期
            if weekday == '*' or weekday == '?':
                weekday_desc = ""
            else:
                weekday_names = ['周日', '周一', '周二', '周三', '周四', '周五', '周六', '周日']
                if weekday.isdigit():
                    wd = int(weekday)
                    if 0 <= wd <= 7:
                        weekday_desc = f"在{weekday_names[wd]}"
                    else:
                        weekday_desc = f"在星期{weekday}"
                else:
                    weekday_desc = f"在星期{weekday}"
            
            # 组合描述
            if weekday_desc:
                if day == '*' and month == '*':
                    desc = f"{hour_desc}{minute_desc}，{weekday_desc}"
                else:
                    desc = f"{month_desc}{day_desc}{weekday_desc}，{hour_desc}{minute_desc}"
            else:
                desc = f"{month_desc}{day_desc}，{hour_desc}{minute_desc}"
            
            return desc
            
        except Exception:
            return "复杂的Cron表达式"
    
    def _get_result(self) -> Dict[str, Any]:
        """获取解析结果"""
        return {
            'valid': self.is_valid,
            'expression': self.expression,
            'fields': self.fields,
            'description': self.description,
            'error': self.error_message
        }
    
    def get_next_run_times(self, expression: str, count: int = 5, base_time: Optional[datetime] = None) -> List[datetime]:
        """
        获取接下来的执行时间
        
        Args:
            expression: Cron表达式
            count: 获取的时间数量
            base_time: 基准时间，默认为当前时间
            
        Returns:
            执行时间列表
        """
        # 获取日志记录器
        logger = logging.getLogger('cron_parser')
        
        logger.debug(f"开始计算下次执行时间列表，Cron表达式: {expression}, 数量: {count}")
        
        if base_time is None:
            base_time = timezone_now()
            logger.debug(f"使用当前时间作为基准时间: {base_time}")
        else:
            logger.debug(f"使用指定的基准时间: {base_time}")
        
        try:
            # 处理预定义表达式
            if expression in self.PREDEFINED_EXPRESSIONS:
                original_expression = expression
                expression = self.PREDEFINED_EXPRESSIONS[expression]
                logger.debug(f"将预定义表达式 {original_expression} 转换为: {expression}")
            
            # 确保base_time是naive datetime（croniter需要）
            if hasattr(base_time, 'tzinfo') and base_time.tzinfo:
                base_time_naive = base_time.replace(tzinfo=None)
                logger.debug(f"将aware datetime转换为naive datetime: {base_time} -> {base_time_naive}")
            else:
                base_time_naive = base_time
                logger.debug(f"base_time已经是naive datetime: {base_time_naive}")
            
            logger.debug(f"创建croniter对象，Cron表达式: {expression}, 基准时间: {base_time_naive}")
            cron = croniter(expression, base_time_naive)
            
            result = []
            for i in range(count):
                next_time = cron.get_next(datetime)
                logger.debug(f"计算得到的第{i+1}次执行时间: {next_time}")
                result.append(next_time)
            
            logger.debug(f"成功计算得到{len(result)}个执行时间")
            return result
        except Exception as e:
            logger.error(f"计算下次执行时间列表失败: {str(e)}")
            logger.debug(f"异常详情: {traceback.format_exc()}")
            
            # 记录详细的系统错误日志
            log_error(
                "cron_parser_get_next_run_times_failed",
                {
                    "expression": expression,
                    "count": count,
                    "base_time": base_time.isoformat() if base_time else None,
                    "error_type": type(e).__name__,
                    "error_message": str(e),
                    "stack_trace": traceback.format_exc(),
                    "execution_context": {
                        "function": "get_next_run_times",
                        "module": "scheduler.cron_parser",
                        "class": "CronParser"
                    }
                }
            )
            
            return []
    
    def is_time_to_run(self, expression: str, check_time: Optional[datetime] = None) -> bool:
        """
        检查指定时间是否应该执行任务
        
        Args:
            expression: Cron表达式
            check_time: 检查的时间，默认为当前时间
            
        Returns:
            是否应该执行
        """
        # 获取日志记录器
        logger = logging.getLogger('cron_parser')
        
        logger.debug(f"检查是否应该执行任务，Cron表达式: {expression}")
        
        if check_time is None:
            check_time = timezone_now()
            logger.debug(f"使用当前时间作为检查时间: {check_time}")
        else:
            logger.debug(f"使用指定的检查时间: {check_time}")
        
        try:
            # 处理预定义表达式
            if expression in self.PREDEFINED_EXPRESSIONS:
                original_expression = expression
                expression = self.PREDEFINED_EXPRESSIONS[expression]
                logger.debug(f"将预定义表达式 {original_expression} 转换为: {expression}")
            
            # 确保check_time是naive datetime（croniter需要）
            if hasattr(check_time, 'tzinfo') and check_time.tzinfo:
                check_time_naive = check_time.replace(tzinfo=None)
                logger.debug(f"将aware datetime转换为naive datetime: {check_time} -> {check_time_naive}")
            else:
                check_time_naive = check_time
                logger.debug(f"check_time已经是naive datetime: {check_time_naive}")
            
            logger.debug(f"创建croniter对象，Cron表达式: {expression}, 检查时间: {check_time_naive}")
            # 获取上一次执行时间
            cron = croniter(expression, check_time_naive)
            prev_time = cron.get_prev(datetime)
            logger.debug(f"计算得到的上一次执行时间: {prev_time}")
            
            # 检查是否在同一分钟内（使用naive datetime进行比较）
            should_run = (check_time_naive.year == prev_time.year and
                    check_time_naive.month == prev_time.month and
                    check_time_naive.day == prev_time.day and
                    check_time_naive.hour == prev_time.hour and
                    check_time_naive.minute == prev_time.minute)
            
            logger.debug(f"检查时间: {check_time_naive}, 上一次执行时间: {prev_time}, 是否应该执行: {should_run}")
            if should_run:
                logger.info(f"任务应该执行，Cron表达式: {expression}, 检查时间: {check_time}")
            else:
                # 计算下一次执行时间，用于调试
                next_time = cron.get_next(datetime)
                logger.debug(f"下一次执行时间: {next_time}")
            
            return should_run
        except Exception as e:
            logger.error(f"检查是否应该执行任务失败: {str(e)}")
            logger.debug(f"异常详情: {traceback.format_exc()}")
            
            # 记录详细的系统错误日志
            log_error(
                "cron_parser_is_time_to_run_failed",
                {
                    "expression": expression,
                    "check_time": check_time.isoformat() if check_time else None,
                    "error_type": type(e).__name__,
                    "error_message": str(e),
                    "stack_trace": traceback.format_exc(),
                    "execution_context": {
                        "function": "is_time_to_run",
                        "module": "scheduler.cron_parser",
                        "class": "CronParser"
                    }
                }
            )
            
            return False


# 全局解析器实例
cron_parser = CronParser()


def parse_cron_expression(expression: str) -> Dict[str, Any]:
    """解析Cron表达式的便捷函数"""
    return cron_parser.parse(expression)


def validate_cron_expression(expression: str) -> bool:
    """验证Cron表达式的便捷函数"""
    result = cron_parser.parse(expression)
    return result['valid']


def get_cron_description(expression: str) -> str:
    """获取Cron表达式描述的便捷函数"""
    result = cron_parser.parse(expression)
    return result['description'] if result['valid'] else result['error']


def get_next_run_times(expression: str, count: int = 5) -> List[datetime]:
    """获取下次执行时间的便捷函数"""
    return cron_parser.get_next_run_times(expression, count)


# 常用的Cron表达式示例
CRON_EXAMPLES = [
    {
        'expression': '0 0 * * *',
        'description': '每天午夜执行',
        'category': '每日'
    },
    {
        'expression': '0 */2 * * *',
        'description': '每2小时执行一次',
        'category': '每小时'
    },
    {
        'expression': '0 0 * * 1',
        'description': '每周一午夜执行',
        'category': '每周'
    },
    {
        'expression': '0 0 1 * *',
        'description': '每月1号午夜执行',
        'category': '每月'
    },
    {
        'expression': '*/5 * * * *',
        'description': '每5分钟执行一次',
        'category': '每分钟'
    },
    {
        'expression': '0 9 * * 1-5',
        'description': '工作日上午9点执行',
        'category': '工作日'
    },
    {
        'expression': '0 2 1 */2 *',
        'description': '每两个月1号凌晨2点执行',
        'category': '复杂'
    }
]